﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Chat
{
    /// <summary>
    /// Thrown upon attempt to create a NickName instanse using illegal characters.
    /// </summary>
    public class NickNameException : ApplicationException
    {
        public NickNameException(string message) : base(message)
        {
        }
    }

    /// <summary>
    /// The chat nickname. Incapsulates the policy for valid nick name names.
    /// It is impossible to create an instanse of this class with nick name,
    /// that violates the nick name naming policy.
    /// The IEquatable interface and the GetHashCode() method are implemented
    /// in a way, that makes the nick name values case insensitive in related
    /// operations.
    /// </summary>
    [Serializable]
    public class NickName : IComparable<NickName>, IEquatable<NickName>
    {
        /// <summary>
        /// The nick name value.
        /// </summary>
        private string value;

        /// <summary>
        /// The regular expression, determing the naming policy, which is the common
        /// policy of alphanumeric characters some symbols (like _) etc...
        /// Stollen from the http://regexlib.com/
        /// </summary>
        private static readonly string nickNameRegEx =
            @"^([a-zA-Z])[a-zA-Z_-]*[\w_-]*[\S]$|^([a-zA-Z])[0-9_-]*[\S]$|^[a-zA-Z]*[\S]$";

        /// <summary>
        /// A special nick name value, used for broadcast messages, i.e. to all
        /// chat clients.
        /// </summary>
        public static readonly NickName Broadcast;

        public NickName(string value)
        {
            Value = value;
        }

        static NickName()
        {
            Broadcast = new NickName("DAD35C5E-A921-4e8d-9C64-17E7A0A88865");
        }

        public string Value
        {
            get { return this.value; }

            set
            {
                if (!Regex.IsMatch(value, nickNameRegEx))
                    throw new NickNameException("Invalid nickname");

                this.value = value;
            }
        }

        public static bool operator ==(NickName value1, NickName value2)
        {
            return value1.Equals(value2);
        }

        public static bool operator !=(NickName value1, NickName value2)
        {
            return ! value1.Equals(value2);
        }

        #region IComparable<NickName> Members

        public int CompareTo(NickName other)
        {
            return Value.CompareTo(other.Value);
        }

        #endregion

        #region IEquatable<NickName> Members

        public bool Equals(NickName other)
        {
            return Value.Equals(other.Value, StringComparison.CurrentCultureIgnoreCase);

            // return string.Compare(Value, other.Value, true) == 0;
        }

        #endregion

        // override object.Equals
        public override bool Equals(object obj)
        {
            //       
            // See the full list of guidelines at
            //   http://go.microsoft.com/fwlink/?LinkID=85237  
            // and also the guidance for operator== at
            //   http://go.microsoft.com/fwlink/?LinkId=85238
            //

            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }

            return Equals(obj as NickName);
        }

        // override object.GetHashCode
        /// <summary>
        /// The hash code is calculated after all the nick name characters
        /// are put in lower case. This gives the ability to use case
        /// insensitive nick name values as keys in Dictionary generic class.
        /// </summary>
        /// <returns>The hash code of the NickName instanse.</returns>
        public override int GetHashCode()
        {
            return Value.ToLower().GetHashCode();
        }

        public override string ToString()
        {
            return Value;
        }
    }
}
